package cn.sunshine.ymcc.service.impl;


import cn.sunshine.ymcc.dto.RedisSMSCodeValueDTO;
import cn.sunshine.dto.UserRegisterDTO;
import cn.sunshine.fegin.UaaFeignClient;
import cn.sunshine.ymcc.domain.Login;
import cn.sunshine.ymcc.domain.User;
import cn.sunshine.ymcc.domain.UserAccount;
import cn.sunshine.ymcc.domain.UserBaseInfo;
import cn.sunshine.ymcc.constant.RedisConstant;
import cn.sunshine.ymcc.mapper.UserAccountMapper;
import cn.sunshine.ymcc.mapper.UserBaseInfoMapper;
import cn.sunshine.ymcc.mapper.UserMapper;
import cn.sunshine.ymcc.result.JSONResult;
import cn.sunshine.ymcc.service.IUserService;
import cn.sunshine.ymcc.util.AssertUtil;
import cn.sunshine.ymcc.util.BitStatesUtils;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

/**
 * <p>
 * 会员登录账号 服务实现类
 * </p>
 *
 * @author Eric Chen
 * @since 2022-08-04
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Autowired
    private  UserMapper userMapper;


    @Autowired
    private UserBaseInfoMapper userBaseInfoMapper;

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private UaaFeignClient uaaFeignClient;

    /**
     *
     * 用户登录
     * 1.参数校验(@valid)
     * 	2.参数判断
     * 		mobile---查数据库  有没有这个手机号
     * 				有  抛异常
     * 		smsCode---到redis里面去查  看是不是一样
     * 				如果不一样  抛异常
     * 	3.因为user需要存loginId  所以先存login表
     * 		存好之后需要给我一个loginId
     * 	4.保存user相关的表
     * 		user
     * 		user-base-info
     * 		user-account
     * 	5.删除redis中对应的手机验证码
     * @param dto
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class) // 开启Seata全局事务
    public void register(UserRegisterDTO dto) {

        AssertUtil.isFalse(existUserByPhone(dto.getMobile()), "用户已注册");
        RedisSMSCodeValueDTO smsCodeRedisValueByPhone = getSMSCodeRedisValueByPhone(dto.getMobile());
        String code = smsCodeRedisValueByPhone.getCode();
        AssertUtil.isNotNull(code,"没有找到验证码");
        AssertUtil.isTrue(checkSmsCode(smsCodeRedisValueByPhone.getCode(),dto.getSmsCode()),"验证码错误");
        Long loginId = insertLoginAndGetLoginId(dto);
        insertRelateUserTables(dto,loginId);
        //删除redis中对应的手机验证码
        removeSMSCodeRedisKey(dto.getMobile());
    }


    /**
     * 根据传入的手机号  判断是否已经存在该用户
     * @param phone  手机号
     * @return
     */
    boolean existUserByPhone(String phone){
        EntityWrapper<User> wrapper = new EntityWrapper<>();
        wrapper.eq("phone", phone);
        List<User> userList = userMapper.selectList(wrapper);
        return userList != null && userList.size() > 0;
    }


    /**
     * 根据传入的手机号  判断redis中是否有key
     * @param phone
     * @return
     */
    RedisSMSCodeValueDTO getSMSCodeRedisValueByPhone(String phone){
        String key = RedisConstant.REDIS_SMS_CODE_PREFIX + phone;
        Object redisSMSCodeValueDTO = redisTemplate.opsForValue().get(key);
        System.out.println(redisSMSCodeValueDTO.toString());
        return (RedisSMSCodeValueDTO)redisSMSCodeValueDTO;
        // return (RedisSMSCodeValueDTO) redisTemplate.opsForValue().get(key);
    }


    /**
     * 验证用户传入的短信验证码和redis中的是否一
     * @param smsCode
     * @return
     */
    boolean checkSmsCode(String redisCode , String smsCode){
        return StringUtils.equals(redisCode, smsCode);
    }

    /**
     * 插入login表  并返回loginId
     * @param dto
     * @return
     */
    Long insertLoginAndGetLoginId(UserRegisterDTO dto){
        Login login = new Login();
        login.setUsername(dto.getMobile());
        login.setPassword(dto.getPassword());
        login.setType(dto.getRegChannel());
        JSONResult loginSaveResult = uaaFeignClient.saveOrUpdate(login);
        Object data = loginSaveResult.getData();
        ObjectMapper objectMapper = new ObjectMapper();
        Login login1 = objectMapper.convertValue(data, Login.class);

        return login1.getId();
    }



    /**
     * 插入用户相关的表
     * user表
     * user_base_info表
     * user_account表
     *
     * @param dto
     * @param loginId
     */
    void insertRelateUserTables(UserRegisterDTO dto , Long loginId){
        User user = insertUserAndGetUser(dto,loginId);
        insertUserBaseInfo(user,dto);
        //int sun = 1/0;(测试seata的回滚)
        insertUserAccount(user);
    }


    /**
     * 插入user表  并返回user对象
     * @param dto
     * @param loginId
     * @return
     */
    User insertUserAndGetUser(UserRegisterDTO dto , Long loginId){
        User user = new User();
        user.setPhone(dto.getMobile());
        user.setCreateTime(new Date().getTime());
        user.setNickName(dto.getMobile());
        Long states = BitStatesUtils.batchAddState(0L,
                BitStatesUtils.OP_REGISTED,
                BitStatesUtils.OP_ACTIVED,
                BitStatesUtils.OP_AUTHED_PHONE
        );
        user.setBitState(states);
        user.setLoginId(loginId);
        userMapper.insert(user);
        return user;
    }

    /**
     * 插入user base info 表
     * @param user
     * @param dto
     */
    void insertUserBaseInfo(User user , UserRegisterDTO dto){
        UserBaseInfo userBaseInfo = new UserBaseInfo();
        userBaseInfo.setId(user.getId());
        userBaseInfo.setCreateTime(user.getCreateTime());
        userBaseInfo.setRegChannel(dto.getRegChannel());
        userBaseInfoMapper.insert(userBaseInfo);
    }


    /**
     * 插入user account 表
     * @param user
     */
    void insertUserAccount(User user){
        UserAccount userAccount = new UserAccount();
        userAccount.setId(user.getId());
        userAccount.setCreateTime(user.getCreateTime());
        userAccountMapper.insert(userAccount);
    }

    void removeSMSCodeRedisKey(String phone){
        redisTemplate.delete(RedisConstant.REDIS_SMS_CODE_PREFIX + phone);
    }



}
